package qlsl.androiddesign.view.listview.swipe;

/*
 * Copyright (C) 2013 47 Degrees, LLC
 * http://47deg.com
 * hello@47deg.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.List;

import com.qlsl.androiddesign.appname.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;

/**
 * ListView subclass that provides the swipe functionality
 */
public class SwipeListView extends ListView {

	/**
	 * log tag
	 */
	public final static String TAG = "SwipeListView";

	/**
	 * whether debug
	 */
	public final static boolean DEBUG = false;

	/**
	 * Used when user want change swipe list mode on some rows
	 */
	public final static int SWIPE_MODE_DEFAULT = -1;

	/**
	 * Disables all swipes
	 */
	public final static int SWIPE_MODE_NONE = 0;

	/**
	 * Enables both left and right swipe
	 */
	public final static int SWIPE_MODE_BOTH = 1;

	/**
	 * Enables right swipe
	 */
	public final static int SWIPE_MODE_RIGHT = 2;

	/**
	 * Enables left swipe
	 */
	public final static int SWIPE_MODE_LEFT = 3;

	/**
	 * Binds the swipe gesture to reveal a view behind the row (Drawer style)
	 */
	public final static int SWIPE_ACTION_REVEAL = 0;

	/**
	 * Dismisses the cell when swiped over
	 */
	public final static int SWIPE_ACTION_DISMISS = 1;

	/**
	 * Marks the cell as checked when swiped and release
	 */
	public final static int SWIPE_ACTION_CHOICE = 2;

	/**
	 * No action when swiped
	 */
	public final static int SWIPE_ACTION_NONE = 3;

	/**
	 * Default ids for front view
	 */
	public final static String SWIPE_DEFAULT_FRONT_VIEW = "swipelist_frontview";

	/**
	 * Default id for back view
	 */
	public final static String SWIPE_DEFAULT_BACK_VIEW = "swipelist_backview";

	/**
	 * Indicates no movement
	 */
	private final static int TOUCH_STATE_REST = 0;

	/**
	 * State scrolling x position
	 */
	private final static int TOUCH_STATE_SCROLLING_X = 1;

	/**
	 * State scrolling y position
	 */
	private final static int TOUCH_STATE_SCROLLING_Y = 2;

	private int touchState = TOUCH_STATE_REST;

	private float lastMotionX;
	private float lastMotionY;
	protected int touchSlop;

	int swipeFrontView = 0;
	int swipeBackView = 0;

	/**
	 * Internal listener for common swipe events
	 */
	private SwipeListViewListener swipeListViewListener;

	/**
	 * Internal touch listener
	 */
	protected SwipeListViewTouchListener touchListener;

	/**
	 * If you create a View programmatically you need send back and front
	 * identifier
	 * 
	 * @param context
	 *            Context
	 * @param swipeBackView
	 *            Back Identifier
	 * @param swipeFrontView
	 *            Front Identifier
	 */
	public SwipeListView(Context context, int swipeBackView, int swipeFrontView) {
		super(context);
		this.swipeFrontView = swipeFrontView;
		this.swipeBackView = swipeBackView;
		init(null);
	}

	/**
	 * @see android.widget.ListView#ListView(android.content.Context,
	 *      android.util.AttributeSet)
	 */
	public SwipeListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(attrs);
	}

	/**
	 * @see android.widget.ListView#ListView(android.content.Context,
	 *      android.util.AttributeSet, int)
	 */
	public SwipeListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(attrs);
	}

	/**
	 * Init ListView
	 * 
	 * @param attrs
	 *            AttributeSet
	 */
	protected void init(AttributeSet attrs) {

		int swipeMode = SWIPE_MODE_BOTH;
		boolean swipeOpenOnLongPress = true;
		boolean swipeCloseAllItemsWhenMoveList = true;
		long swipeAnimationTime = 0;
		float swipeOffsetLeft = 0;
		float swipeOffsetRight = 0;
		int swipeDrawableChecked = 0;
		int swipeDrawableUnchecked = 0;

		int swipeActionLeft = SWIPE_ACTION_REVEAL;
		int swipeActionRight = SWIPE_ACTION_REVEAL;

		if (attrs != null) {
			TypedArray styled = getContext().obtainStyledAttributes(attrs, R.styleable.SwipeListView);
			swipeMode = styled.getInt(R.styleable.SwipeListView_swipeMode, SWIPE_MODE_BOTH);
			swipeActionLeft = styled.getInt(R.styleable.SwipeListView_swipeActionLeft, SWIPE_ACTION_REVEAL);
			swipeActionRight = styled.getInt(R.styleable.SwipeListView_swipeActionRight, SWIPE_ACTION_REVEAL);
			swipeOffsetLeft = styled.getDimension(R.styleable.SwipeListView_swipeOffsetLeft, 0);
			swipeOffsetRight = styled.getDimension(R.styleable.SwipeListView_swipeOffsetRight, 0);
			swipeOpenOnLongPress = styled.getBoolean(R.styleable.SwipeListView_swipeOpenOnLongPress, true);
			swipeAnimationTime = styled.getInteger(R.styleable.SwipeListView_swipeAnimationTime, 0);
			swipeCloseAllItemsWhenMoveList = styled.getBoolean(R.styleable.SwipeListView_swipeCloseAllItemsWhenMoveList,
					true);
			swipeDrawableChecked = styled.getResourceId(R.styleable.SwipeListView_swipeDrawableChecked, 0);
			swipeDrawableUnchecked = styled.getResourceId(R.styleable.SwipeListView_swipeDrawableUnchecked, 0);
			swipeFrontView = styled.getResourceId(R.styleable.SwipeListView_swipeFrontView, 0);
			swipeBackView = styled.getResourceId(R.styleable.SwipeListView_swipeBackView, 0);
			styled.recycle();
		}

		if (swipeFrontView == 0 || swipeBackView == 0) {
			swipeFrontView = getContext().getResources().getIdentifier(SWIPE_DEFAULT_FRONT_VIEW, "id",
					getContext().getPackageName());
			swipeBackView = getContext().getResources().getIdentifier(SWIPE_DEFAULT_BACK_VIEW, "id",
					getContext().getPackageName());

			if (swipeFrontView == 0 || swipeBackView == 0) {
				throw new RuntimeException(String.format(
						"You forgot the attributes swipeFrontView or swipeBackView. You can add this attributes or use '%s' and '%s' identifiers",
						SWIPE_DEFAULT_FRONT_VIEW, SWIPE_DEFAULT_BACK_VIEW));

			}
		}

		final ViewConfiguration configuration = ViewConfiguration.get(getContext());
		touchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
		touchListener = new SwipeListViewTouchListener(this, swipeFrontView, swipeBackView);
		if (swipeAnimationTime > 0) {
			touchListener.setAnimationTime(swipeAnimationTime);
		}
		touchListener.setRightOffset(swipeOffsetRight);
		touchListener.setLeftOffset(swipeOffsetLeft);
		touchListener.setSwipeActionLeft(swipeActionLeft);
		touchListener.setSwipeActionRight(swipeActionRight);
		touchListener.setSwipeMode(swipeMode);
		touchListener.setSwipeClosesAllItemsWhenListMoves(swipeCloseAllItemsWhenMoveList);
		touchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress);
		touchListener.setSwipeDrawableChecked(swipeDrawableChecked);
		touchListener.setSwipeDrawableUnchecked(swipeDrawableUnchecked);
		setOnTouchListener(touchListener);
		setOnScrollListener(touchListener.makeScrollListener());
	}

	/**
	 * Recycle cell. This method should be called from getView in Adapter when
	 * use SWIPE_ACTION_CHOICE
	 * 
	 * @param convertView
	 *            parent view
	 * @param position
	 *            position in list
	 */
	public void recycle(View convertView, int position) {
		touchListener.reloadChoiceStateInView(convertView.findViewById(swipeFrontView), position);
		touchListener.reloadSwipeStateInView(convertView.findViewById(swipeFrontView), position);

		// Clean pressed state (if dismiss is fire from a cell, to this cell,
		// with a press drawable, in a swipelistview
		// when this cell will be recycle it will still have his pressed state.
		// This ensure the pressed state is
		// cleaned.
		for (int j = 0; j < ((ViewGroup) convertView).getChildCount(); ++j) {
			View nextChild = ((ViewGroup) convertView).getChildAt(j);
			nextChild.setPressed(false);
		}
	}

	/**
	 * Get if item is selected
	 * 
	 * @param position
	 *            position in list
	 * @return
	 */
	public boolean isChecked(int position) {
		return touchListener.isChecked(position);
	}

	/**
	 * Get positions selected
	 * 
	 * @return
	 */
	public List<Integer> getPositionsSelected() {
		return touchListener.getPositionsSelected();
	}

	/**
	 * Count selected
	 * 
	 * @return
	 */
	public int getCountSelected() {
		return touchListener.getCountSelected();
	}

	/**
	 * Unselected choice state in item
	 */
	public void unselectedChoiceStates() {
		touchListener.unselectedChoiceStates();
	}

	/**
	 * @see android.widget.ListView#setAdapter(android.widget.ListAdapter)
	 */
	@Override
	public void setAdapter(ListAdapter adapter) {
		super.setAdapter(adapter);
		touchListener.resetItems();
		if (null != adapter) {
			adapter.registerDataSetObserver(new DataSetObserver() {

				@Override
				public void onChanged() {
					super.onChanged();
					onListChanged();
					touchListener.resetItems();
				}
			});
		}
	}

	/**
	 * Dismiss item
	 * 
	 * @param position
	 *            Position that you want open
	 */
	public void dismiss(int position) {
		int height = touchListener.dismiss(position);
		if (height > 0) {
			touchListener.handlerPendingDismisses(height);
		} else {
			int[] dismissPositions = new int[1];
			dismissPositions[0] = position;
			onDismiss(dismissPositions);
			touchListener.resetPendingDismisses();
		}
	}

	/**
	 * Dismiss items selected
	 */
	public void dismissSelected() {
		List<Integer> list = touchListener.getPositionsSelected();
		int[] dismissPositions = new int[list.size()];
		int height = 0;
		for (int i = 0; i < list.size(); i++) {
			int position = list.get(i);
			dismissPositions[i] = position;
			int auxHeight = touchListener.dismiss(position);
			if (auxHeight > 0) {
				height = auxHeight;
			}
		}
		if (height > 0) {
			touchListener.handlerPendingDismisses(height);
		} else {
			onDismiss(dismissPositions);
			touchListener.resetPendingDismisses();
		}
		touchListener.returnOldActions();
	}

	/**
	 * Open ListView's item
	 * 
	 * @param position
	 *            Position that you want open
	 */
	public void openAnimate(int position) {
		touchListener.openAnimate(position);
	}

	/**
	 * Close ListView's item
	 * 
	 * @param position
	 *            Position that you want open
	 */
	public void closeAnimate(int position) {
		touchListener.closeAnimate(position);
	}

	/**
	 * Notifies onDismiss
	 * 
	 * @param reverseSortedPositions
	 *            All dismissed positions
	 */
	protected void onDismiss(int[] reverseSortedPositions) {
		if (swipeListViewListener != null) {
			swipeListViewListener.onDismiss(reverseSortedPositions);
		}
	}

	/**
	 * Start open item
	 * 
	 * @param position
	 *            list item
	 * @param action
	 *            current action
	 * @param right
	 *            to right
	 */
	protected void onStartOpen(int position, int action, boolean right) {
		if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
			swipeListViewListener.onStartOpen(position, action, right);
		}
	}

	/**
	 * Start close item
	 * 
	 * @param position
	 *            list item
	 * @param right
	 */
	protected void onStartClose(int position, boolean right) {
		if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
			swipeListViewListener.onStartClose(position, right);
		}
	}

	/**
	 * Notifies onClickFrontView
	 * 
	 * @param position
	 *            item clicked
	 */
	protected void onClickFrontView(int position) {
		if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
			swipeListViewListener.onClickFrontView(position);
		}
	}

	/**
	 * Notifies onClickBackView
	 * 
	 * @param position
	 *            back item clicked
	 */
	protected void onClickBackView(int position) {
		if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
			swipeListViewListener.onClickBackView(position);
		}
	}

	/**
	 * Notifies onOpened
	 * 
	 * @param position
	 *            Item opened
	 * @param toRight
	 *            If should be opened toward the right
	 */
	protected void onOpened(int position, boolean toRight) {
		if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
			swipeListViewListener.onOpened(position, toRight);
		}
	}

	/**
	 * Notifies onClosed
	 * 
	 * @param position
	 *            Item closed
	 * @param fromRight
	 *            If open from right
	 */
	protected void onClosed(int position, boolean fromRight) {
		if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
			swipeListViewListener.onClosed(position, fromRight);
		}
	}

	/**
	 * Notifies onChoiceChanged
	 * 
	 * @param position
	 *            position that choice
	 * @param selected
	 *            if item is selected or not
	 */
	protected void onChoiceChanged(int position, boolean selected) {
		if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
			swipeListViewListener.onChoiceChanged(position, selected);
		}
	}

	/**
	 * User start choice items
	 */
	protected void onChoiceStarted() {
		if (swipeListViewListener != null) {
			swipeListViewListener.onChoiceStarted();
		}
	}

	/**
	 * User end choice items
	 */
	protected void onChoiceEnded() {
		if (swipeListViewListener != null) {
			swipeListViewListener.onChoiceEnded();
		}
	}

	/**
	 * User is in first item of list
	 */
	protected void onFirstListItem() {
		if (swipeListViewListener != null) {
			swipeListViewListener.onFirstListItem();
		}
	}

	/**
	 * User is in last item of list
	 */
	protected void onLastListItem() {
		if (swipeListViewListener != null) {
			swipeListViewListener.onLastListItem();
		}
	}

	/**
	 * Notifies onListChanged
	 */
	protected void onListChanged() {
		if (swipeListViewListener != null) {
			swipeListViewListener.onListChanged();
		}
	}

	/**
	 * Notifies onMove
	 * 
	 * @param position
	 *            Item moving
	 * @param x
	 *            Current position
	 */
	protected void onMove(int position, float x) {
		if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
			swipeListViewListener.onMove(position, x);
		}
	}

	protected int changeSwipeMode(int position) {
		if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
			return swipeListViewListener.onChangeSwipeMode(position);
		}
		return SWIPE_MODE_DEFAULT;
	}

	/**
	 * Sets the Listener
	 * 
	 * @param swipeListViewListener
	 *            Listener
	 */
	public void setSwipeListViewListener(SwipeListViewListener swipeListViewListener) {
		this.swipeListViewListener = swipeListViewListener;
	}

	/**
	 * Resets scrolling
	 */
	public void resetScrolling() {
		touchState = TOUCH_STATE_REST;
	}

	/**
	 * Set offset on right
	 * 
	 * @param offsetRight
	 *            Offset
	 */
	public void setOffsetRight(float offsetRight) {
		touchListener.setRightOffset(offsetRight);
	}

	/**
	 * Set offset on left
	 * 
	 * @param offsetLeft
	 *            Offset
	 */
	public void setOffsetLeft(float offsetLeft) {
		touchListener.setLeftOffset(offsetLeft);
	}

	/**
	 * Set if all items opened will be closed when the user moves the ListView
	 * 
	 * @param swipeCloseAllItemsWhenMoveList
	 */
	public void setSwipeCloseAllItemsWhenMoveList(boolean swipeCloseAllItemsWhenMoveList) {
		touchListener.setSwipeClosesAllItemsWhenListMoves(swipeCloseAllItemsWhenMoveList);
	}

	/**
	 * Sets if the user can open an item with long pressing on cell
	 * 
	 * @param swipeOpenOnLongPress
	 */
	public void setSwipeOpenOnLongPress(boolean swipeOpenOnLongPress) {
		touchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress);
	}

	/**
	 * Set swipe mode
	 * 
	 * @param swipeMode
	 */
	public void setSwipeMode(int swipeMode) {
		touchListener.setSwipeMode(swipeMode);
	}

	/**
	 * 获取滑动模式,by ylq at 20160303<br/>
	 */
	public int getSwipeMode() {
		return touchListener.getSwipeMode();
	}

	/**
	 * Return action on left
	 * 
	 * @return Action
	 */
	public int getSwipeActionLeft() {
		return touchListener.getSwipeActionLeft();
	}

	/**
	 * Set action on left
	 * 
	 * @param swipeActionLeft
	 *            Action
	 */
	public void setSwipeActionLeft(int swipeActionLeft) {
		touchListener.setSwipeActionLeft(swipeActionLeft);
	}

	/**
	 * Return action on right
	 * 
	 * @return Action
	 */
	public int getSwipeActionRight() {
		return touchListener.getSwipeActionRight();
	}

	/**
	 * Set action on right
	 * 
	 * @param swipeActionRight
	 *            Action
	 */
	public void setSwipeActionRight(int swipeActionRight) {
		touchListener.setSwipeActionRight(swipeActionRight);
	}

	/**
	 * Sets animation time when user drops cell
	 * 
	 * @param animationTime
	 *            milliseconds
	 */
	public void setAnimationTime(long animationTime) {
		touchListener.setAnimationTime(animationTime);
	}

	/**
	 * @see android.widget.ListView#onInterceptTouchEvent(android.view.MotionEvent)
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		int action = MotionEventCompat.getActionMasked(ev);
		final float x = ev.getX();
		final float y = ev.getY();

		if (isEnabled() && touchListener.isSwipeEnabled()) {

			if (touchState == TOUCH_STATE_SCROLLING_X) {
				return touchListener.onTouch(this, ev);
			}

			switch (action) {
			case MotionEvent.ACTION_MOVE:
				checkInMoving(x, y);
				return touchState == TOUCH_STATE_SCROLLING_Y;
			case MotionEvent.ACTION_DOWN:
				super.onInterceptTouchEvent(ev);
				touchListener.onTouch(this, ev);
				touchState = TOUCH_STATE_REST;
				lastMotionX = x;
				lastMotionY = y;
				return false;
			case MotionEvent.ACTION_CANCEL:
				touchState = TOUCH_STATE_REST;
				break;
			case MotionEvent.ACTION_UP:
				touchListener.onTouch(this, ev);
				return touchState == TOUCH_STATE_SCROLLING_Y;
			default:
				break;
			}
		}

		return super.onInterceptTouchEvent(ev);
	}

	/**
	 * Check if the user is moving the cell
	 * 
	 * @param x
	 *            Position X
	 * @param y
	 *            Position Y
	 */
	private void checkInMoving(float x, float y) {
		final int xDiff = (int) Math.abs(x - lastMotionX);
		final int yDiff = (int) Math.abs(y - lastMotionY);

		final int touchSlop = this.touchSlop;
		boolean xMoved = xDiff > touchSlop;
		boolean yMoved = yDiff > touchSlop;

		if (xMoved) {
			touchState = TOUCH_STATE_SCROLLING_X;
			lastMotionX = x;
			lastMotionY = y;
		}

		if (yMoved) {
			touchState = TOUCH_STATE_SCROLLING_Y;
			lastMotionX = x;
			lastMotionY = y;
		}
	}

	/**
	 * Close all opened items
	 */
	public void closeOpenedItems() {
		touchListener.closeOpenedItems();
	}

	public void resetOpened() {
		touchListener.resetOpened();
	}

}
